/*
 * PipedCommand.cpp
 *
 *  Created on: Jun 7, 2011
 *      Author: s138077
 */

#include <vector>
#include <string>
#include "Command.h"

#include "PipedCommand.h"

PipedCommand::PipedCommand(std::string name, std::vector<std::string>* pathVariables) : Command(name, pathVariables){
	commands = new std::vector<Command*>();
}

PipedCommand::~PipedCommand() {
	for(std::vector<Command*>::iterator i = commands->begin(); i != commands->end(); i++) {
		delete (*i);
	}
	commands->erase(commands->begin(), commands->end());
	delete commands;
}

void PipedCommand::addCommand(Command* command) {
	if(command != NULL) {
		commands->push_back(command);
	}
}

bool PipedCommand::runCommand(bool forked) {
	if(commands->size() > 1) {
		return executeCommand(forked);
	} else {
		return true;
	}
}

bool PipedCommand::runCommand() {
	return runCommand(true);
}

bool PipedCommand::executeCommand(bool forked) {
	int commandListSize = commands->size();

	if(commandListSize > 1) {
		enum {R,W,N};
		std::vector<int>* childs = new std::vector<int>();

		int pipeline[commandListSize-1][N];
		for(int i = 0; i < (commandListSize-1); i++) {
			if (pipe2(pipeline[i], O_CLOEXEC)<0) {perror("pipe"); break;}
		}

		for(int i = 0; i < commandListSize; i++) {
			int child = fork();
			if (child<0) {
				perror("fork"); //TODO : stop all programs already running executed by this method? how does a shell usually handle this?
			} else if (child==0) {
				if(i > 0) {
					//close(STDIN_FILENO);
					dup2(pipeline[i-1][R], STDIN_FILENO); 	//copy input[READ] to CIN (dup2 closes CIN)
					close(pipeline[i-1][R]); 				//close input[READ]
					close(pipeline[i-1][W]);				//close input[WRITE]
				}
				if(i < (commandListSize-1)) {
					//close(STDOUT_FILENO);
					dup2(pipeline[i][W], STDOUT_FILENO); 	//copy output[WRITE] to COUT (dup2 closes COUT)
					close(pipeline[i][R]); 					//close output[READ]
					close(pipeline[i][W]);					//close output[WRITE]
				}
				if(raise(SIGSTOP) == 0) {
//					if(commands->at(i) == (*(--commands->end())))
//						commands->at(i)->runCommand(forked);
//					else
//						commands->at(i)->runCommand(true);
					commands->at(i)->runCommand(false);
					//execv(command[i], argv[i]);
				} else {
					perror("kill");
				}
				return false;
				//exit(EXIT_FAILURE); //TODO: replace with exit(EXIT_SUCCESS); ?
			} else {
				childs->push_back(child);
			}
		}

		for(int i = 0; i < (commandListSize-1); i++) {
			close(pipeline[i][R]);
			close(pipeline[i][W]);
		}

		enum {PROCESS_HALT, COMMAND_START, PROCESS_EXIT, DONE};

		int progress = PROCESS_HALT;
		int status;
		int child = -1;
		std::vector<int>::iterator last = (--childs->end());

		do {
			for(std::vector<int>::iterator i = childs->begin(); i != childs->end() && progress != DONE; i++) {
				switch (progress) {
				case PROCESS_HALT:
					child = waitpid(*i, &status, WUNTRACED);
					if(i == last) progress = COMMAND_START;
					break;
				case COMMAND_START:
					kill(*i, SIGCONT);
					child = waitpid(*i, &status, WCONTINUED);
					if(i == last) progress = PROCESS_EXIT;
					break;
				case PROCESS_EXIT:
					child = waitpid(*i, &status, WUNTRACED);
					if(i == last) progress = DONE;
					break;
				}

				if (child == -1) {
					perror("waitpid");
					exit(EXIT_FAILURE);
				}

//				do {
//					if (WIFEXITED(status)) {
//						cout << "Child exited: " << child << endl << flush;
//						printf("\texited, status=%d\n", WEXITSTATUS(status));
//					} else if (WIFSIGNALED(status)) {
//						cout << "Child signaled: " << child << endl << flush;
//						printf("\tkilled by signal %d\n", WTERMSIG(status));
//					} else if (WIFSTOPPED(status)) {
//						cout << "Child stopped: " << child << endl << flush;
//						printf("\tstopped by signal %d\n", WSTOPSIG(status));
//					} else if (WIFCONTINUED(status)) {
//						cout << "Child continued: " << child << endl << flush;
//						printf("\tcontinued\n");
//					}
//				} while (!WIFEXITED(status) && !WIFSIGNALED(status));
			}
		} while(progress != DONE);

		delete childs;
	}

	return true;
}
